/*
 * EXPLOIT Novell eDirectory evtFilteredMonitorEventsRequest
 * 	Invalid Free Attempt
 * 
 * Copyright (C) 2007 Sourcefire, Inc. All Rights Reserved
 * 
 * Writen by Patrick Mullen <pmullen@sourcefire.com>
 *
 * This file may contain proprietary rules that were created, tested and
 * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as
 * rules that were created by Sourcefire and other third parties and
 * distributed under the GNU General Public License (the "GPL Rules").  The
 * VRT Certified Rules contained in this file are the property of
 * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved.
 * The GPL Rules created by Sourcefire, Inc. are the property of
 * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights
 * Reserved.  All other GPL Rules are owned and copyrighted by their
 * respective owners (please see www.snort.org/contributors for a list of
 * owners and their respective copyrights).  In order to determine what
 * rules are VRT Certified Rules or GPL Rules, please refer to the VRT
 * Certified Rules License Agreement.
 */


#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"
#include "so-util_ber.h"

/* declare detection functions */
int ruleNOVELL_EVENTSREQUEST_FREEeval(void *p);

static RuleReference ruleNOVELL_EVENTSREQUEST_FREEref0 = 
{
    "url", /* type */
    "labs.idefense.com/intelligence/vulnerabilities/display.php?id=428" /* value */
};
static RuleReference ruleNOVELL_EVENTSREQUEST_FREEref1 =
{
    "cve", /* type */
    "2006-4510"
};
static RuleReference ruleNOVELL_EVENTSREQUEST_FREEref2 =
{
    "bugtraq", /* type */
    "20663"
};

static RuleReference *ruleNOVELL_EVENTSREQUEST_FREErefs[] =
{
    &ruleNOVELL_EVENTSREQUEST_FREEref0,
    &ruleNOVELL_EVENTSREQUEST_FREEref1,
    &ruleNOVELL_EVENTSREQUEST_FREEref2,
    NULL
};

static FlowFlags ruleNOVELL_EVENTSREQUEST_FREEflow =
{
    FLOW_ESTABLISHED|FLOW_TO_SERVER
};

static RuleOption ruleNOVELL_EVENTSREQUEST_FREEoption0 =
{
    OPTION_TYPE_FLOWFLAGS,
    {
        &ruleNOVELL_EVENTSREQUEST_FREEflow
    }
};

static ContentInfo ruleNOVELL_EVENTSREQUEST_FREEcontent0 =
{
    (uint8_t *)"|30|",     /* pattern to search for */
    1,                      /* depth */
    0,                      /* offset */
    0,                      /* flags */
    NULL,                   /* holder for boyer/moore info */
    NULL,                   /* holder for byte representation of "NetBus" */
    0,                      /* holder for length of byte representation */
    0                       /* holder of increment length */
};

static RuleOption ruleNOVELL_EVENTSREQUEST_FREEoption1 =
{
    OPTION_TYPE_CONTENT,
    {
        &ruleNOVELL_EVENTSREQUEST_FREEcontent0
    }
};

static ContentInfo ruleNOVELL_EVENTSREQUEST_FREEcontent1 =
{
    (uint8_t *)"2.16.840.1.113719.1.27.100.84",  /* pattern to search for */
    0,                      /* depth */
    0,                      /* offset */
    0,                      /* flags */
    NULL,                   /* holder for boyer/moore info */
    NULL,                   /* holder for byte representation of "NetBus" */
    0,                      /* holder for length of byte representation */
    0                       /* holder of increment length */
};

static RuleOption ruleNOVELL_EVENTSREQUEST_FREEoption2 =
{
    OPTION_TYPE_CONTENT,
    {
        &ruleNOVELL_EVENTSREQUEST_FREEcontent1
    }
};


RuleOption *ruleNOVELL_EVENTSREQUEST_FREEoptions[] =
{
    &ruleNOVELL_EVENTSREQUEST_FREEoption0,
    &ruleNOVELL_EVENTSREQUEST_FREEoption1,
    &ruleNOVELL_EVENTSREQUEST_FREEoption2,
    NULL
};

static RuleMetaData ruleNOVELL_EVENTSREQUEST_FREEservice1 =
{
    "service ldap"
};

static RuleMetaData ruleNOVELL_EVENTSREQUEST_FREEpolicy1 =
{
    "policy max-detect-ips drop"
};

static RuleMetaData *ruleNOVELL_EVENTSREQUEST_FREEmetadata[] =
{
    &ruleNOVELL_EVENTSREQUEST_FREEservice1,
    &ruleNOVELL_EVENTSREQUEST_FREEpolicy1,
    NULL
};

Rule ruleNOVELL_EVENTSREQUEST_FREE = {
   /* rule header */
   {
       IPPROTO_TCP, /* proto */
       "any", /* SRCIP     */
       "any", /* SRCPORT   */
       0, /* DIRECTION */
       HOME_NET, /* DSTIP     */
       "389", /* DSTPORT   */
   },
   /* metadata */
   { 
       3,  /* genid (HARDCODED!!!) */
       13511, /* sigid */
       6, /* revision  */
       "attempted-admin", /* classification, generic */
       0,  /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
       "SERVER-OTHER Novell eDirectory EventsRequest invalid event count exploit attempt",     /* message */
       ruleNOVELL_EVENTSREQUEST_FREErefs /* ptr to references */
        ,ruleNOVELL_EVENTSREQUEST_FREEmetadata
   },
   ruleNOVELL_EVENTSREQUEST_FREEoptions, /* ptr to rule options */
   &ruleNOVELL_EVENTSREQUEST_FREEeval, /* ptr to rule detection function */
   0, /* am I initialized yet? */
   0, /* number of options */
   0  /* don't alert */
};


/* detection functions */

int ruleNOVELL_EVENTSREQUEST_FREEeval(void *p) {
   int retval;

   const uint8_t *cursor_normal, *end_of_payload;

   SFSnortPacket *sp = (SFSnortPacket *) p;

   BER_ELEMENT element;

   uint32_t eventCount, event_sequences_len, extracted_sequences_len;
   uint32_t events_found = 0;

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;

   if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
      return RULE_NOMATCH;

   if((end_of_payload - cursor_normal) < 53)   /* Minimum malicious request length */
      return RULE_NOMATCH;

   /* end_of_payload = sp->payload + sp->payload_size; */

   /* call flow match */
   if(checkFlow(sp, ruleNOVELL_EVENTSREQUEST_FREEoptions[0]->option_u.flowFlags) <= 0 )
      return RULE_NOMATCH;

   /* call content match "|30|" */
   if(contentMatch(sp, ruleNOVELL_EVENTSREQUEST_FREEoptions[1]->option_u.content, &cursor_normal) <= 0) {
      return RULE_NOMATCH;
   }

   /* Find the OID */
   if(contentMatch(sp, ruleNOVELL_EVENTSREQUEST_FREEoptions[2]->option_u.content, &cursor_normal) <= 0) {
      return RULE_NOMATCH;
   }

   /* So at this point, we know the first byte is "|30|" (the universal sequence), we know
      we have an OID, and the cursor is pointing to the byte following the OID.  We are going to
      make the reasonable assumption that if the anchors following this point all match up, the
      portion between the universal sequence and the OID contains the LDAP version and the
      extendedRequest sequence start (0x77 followed by a length field).
   */

   /* requestValue sequence start */
   retval = ber_get_element(p, cursor_normal, &element);

   if(retval < 0 || element.type != 0x81)
      return(RULE_NOMATCH);

   cursor_normal = element.data.data_ptr;

   /* sequence start */
   retval = ber_get_element(p, cursor_normal, &element);

   if(retval < 0 || element.type != 0x30)
      return RULE_NOMATCH;

   cursor_normal = element.data.data_ptr;

   /* eventCount */
   retval = ber_get_element(p, cursor_normal, &element);

   /* Because we're going to extract the int, we need to make sure
      the full data is present
   */
   if((retval < 0) || (element.type != 0x02) || (retval != element.data_len))
      return RULE_NOMATCH;

   /* get value of eventCount */
   retval = ber_extract_int_val(&element);

   if(retval < 0) 
      return RULE_NOMATCH;

   eventCount = element.data.int_val;

   cursor_normal += element.total_len;

   /* Now we need to count the number of elements present to ensure
      it's the same as eventCount.
   */
   /* Check for the event sequences start */
   retval = ber_get_element(p, cursor_normal, &element);

   if(retval < 0 || element.type != 0x31)
      return RULE_NOMATCH;

   cursor_normal = element.data.data_ptr;

   event_sequences_len = element.data_len;
   extracted_sequences_len = 0;

   while(extracted_sequences_len < event_sequences_len) {
      retval = ber_get_element(p, cursor_normal, &element);

      if(retval < 0)
         return RULE_NOMATCH;

      /* If the element type is 0x30, add it to the list */
      if(element.type == 0x30) {
         events_found++;
         extracted_sequences_len += element.specified_total_len;
         cursor_normal += element.total_len;
      } else {
         /* A non-0x30-element inside the list, before the
            data size has been used up.  wtf?
         */
         break;
      }
   }

   if(events_found < eventCount)
      return RULE_MATCH;

   return RULE_NOMATCH; 
}

/*
Rule *rules[] = {
    &ruleNOVELL_EVENTSREQUEST_FREE,
    NULL
};

*/
